#pragma once

#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
#include <unistd.h>

#include "../comm/log.hpp"
#include "../comm/util.hpp"
#include "compiler.hpp"
#include "runner.hpp"

namespace ns_compile_run
{
	using namespace ns_log;
	using namespace ns_util;
	using namespace ns_compiler;
	using namespace ns_runner;
	class CompileAndRun
	{
	public:
		// 清理临时文件
		static void RemoveTempFile(const std::string& file_name)
		{
			//清理文件的个数是不确定的，但是有哪些我们是知道的
			std::string _src = PathUtil::Src(file_name);
			if(FileUtil::IsFileExists(_src)) unlink(_src.c_str());

			std::string _exe = PathUtil::Exe(file_name);
			if(FileUtil::IsFileExists(_exe)) unlink(_exe.c_str());

			std::string _compileerror = PathUtil::CompileError(file_name);
			if(FileUtil::IsFileExists(_compileerror)) unlink(_compileerror.c_str());

			std::string _stdin = PathUtil::Stdin(file_name);
			if(FileUtil::IsFileExists(_stdin)) unlink(_stdin.c_str());

			std::string _stdout = PathUtil::Stdout(file_name);
			if(FileUtil::IsFileExists(_stdout)) unlink(_stdout.c_str());

			std::string _stderr = PathUtil::Stderr(file_name);
			if(FileUtil::IsFileExists(_stderr)) unlink(_stderr.c_str());
		}

		/***************************
		 * code > 0:进程收到了信号导致异常奔溃
		 * code = 0:整个过程全部完成
		 * code < 0:整个过程非运行报错（代码为空，编译报错等）
		 ****************************/
		static std::string CodeToDesc(int status_code, const std::string &file_name)
		{
			std::string desc;
			switch (status_code)
			{
			case 0:
				desc = "编译运行成功！";
				break;
			case -1:
				desc = "提交的代码是空的！";
				break;
			case -2:
				desc = "未知错误！";
				break;
			case -3:
				// 代码在编译时就出现错误，错误信息在.compile_error文件中
				FileUtil::ReadFile(PathUtil::CompileError(file_name), &desc, true);
				break;
			case SIGABRT: // 信号6
				desc = "内存超过范围！";
				break;
			case SIGXCPU: // 信号24
				desc = "cpu运行超时！";
				break;
			case SIGFPE: // 信号8
				desc = "浮点数溢出！";
				break;
			default:
				desc = "未知" + std::to_string(status_code);
				break;
			}
			return desc;
		}

		/***********************************
		 * 输入：
		 * code:用户提交的代码
		 * input:用户给自己提交的代码对应输入，不做处理
		 * cpu_limit:时间要求
		 * men_limit:空间要求
		 *
		 * 输出：
		 * 必填：
		 * status:状态码
		 * reason:请求结果
		 * 选填：
		 * stdout:我的程序运行完的结果
		 * stderr:我的程序运行完的错误结果
		 *
		 * 参数:
		 * in_json: {"code": "#include...", "input": "","cpu_limit":1, "mem_limit":10240}
		 * out_json: {"status":"0", "reason":"","stdout":"","stderr":"",}
		 ***********************************/
		static void start(const std::string &in_json, std::string *out_json)
		{
			// 获取in_json参数
			Json::Value in_value;
			Json::Reader reader;
			reader.parse(in_json, in_value);
			std::string code = in_value["code"].asString();
			std::string inpit = in_value["input"].asString();
			int cpu_limit = in_value["cpu_limit"].asInt();
			int men_limit = in_value["mem_limit"].asInt();

			int status_code = 0;
			Json::Value out_value;
			int run_result = 0;
			std::string file_name; // 需要内部形成的唯一文件名

			if (code.size() == 0)
			{
				status_code = -1; // 代码为空
				goto END;
			}

			// 形成的文件名得具有唯一性，没有目录没有后缀
			// 毫秒级时间戳+原子性递增唯一值: 来保证唯一性
			file_name = FileUtil::UniquFileName();

			// 形成临时的src文件,并且把code写入到src
			if (!FileUtil::WriteFile(PathUtil::Src(file_name), code))
			{
				status_code = -2; // 未知错误
				goto END;
			}

			// 进行编译
			if (!Compiler::compile(file_name))
			{
				// 编译失败
				status_code = -3; // 代码编译发生错误
				goto END;
			}

			// 运行代码
			run_result = Runner::Run(file_name, cpu_limit, men_limit);
			if (run_result < 0)
			{
				status_code = -2; // 未知错误
			}
			else if (run_result > 0)
			{
				status_code = run_result; // 程序运行奔溃
			}
			else
			{
				status_code = 0; // 运行成功
			}
		END:
			out_value["status"] = status_code;
			out_value["reason"] = CodeToDesc(status_code, file_name);
			// 整个过程成功
			if (status_code == 0)
			{
				std::string _stdout;
				FileUtil::ReadFile(PathUtil::Stdout(file_name), &_stdout, true);
				out_value["stdout"] = _stdout;

				std::string _stderr;
				FileUtil::ReadFile(PathUtil::Stderr(file_name), &_stderr, true);
				out_value["stderr"] = _stderr;
			}

			Json::StyledWriter writer;
			*out_json = writer.write(out_value);

			// 删除临时文件
			RemoveTempFile(file_name);
		}
	};
}